package com.xiguaji.tiktok.v1.tiktok.challenge.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiguaji.boot.toolkit.JsonUtil;
import com.xiguaji.boot.toolkit.jdbc.Page;
import com.xiguaji.tiktok.utils.ESUtil;
import com.xiguaji.tiktok.utils.jdbc.PageResponse;
import com.xiguaji.tiktok.v1.tiktok.aweme.dto.BloggerAwemeResponse;
import com.xiguaji.tiktok.v1.tiktok.aweme.repository.AwemeRepository;
import com.xiguaji.tiktok.v1.tiktok.challenge.dto.ChallengeResponse;
import com.xiguaji.tiktok.v1.tiktok.challenge.dto.SearchData;
import com.xiguaji.tiktok.v1.tiktok.collect.model.ChallengeCollection;
import com.xiguaji.tiktok.v1.tiktok.collect.repository.AwemeCollectionRepository;
import com.xiguaji.tiktok.v1.tiktok.collect.repository.ChallengeCollectionRepository;
import com.xiguaji.tiktok.v1.common.service.CommonService;
import com.xiguaji.tiktok.v1.tiktok.music.dto.DistributionRecord;
import com.xiguaji.tiktok.v1.tiktok.music.repository.MusicRepository;
import lombok.val;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class ChallengeService {

    @Autowired
    MusicRepository musicRepository;

    @Autowired
    RestHighLevelClient client;


    @Autowired
    RedisTemplate<String,String> redisTemplate;

    @Autowired
    AwemeRepository awemeRepository;

    @Autowired
    AwemeCollectionRepository awemeCollectionRepository;

    @Autowired
    ESUtil esUtil;

    @Autowired
    CommonService commonService;

    Map<String,String> regionMap = new HashMap<>();

    @Autowired
    ChallengeCollectionRepository challengeCollectionRepository;


    @PostConstruct
    public void postConstruct(){
        commonService.getRegion(null).forEach(regionResponse -> {
            regionMap.put(regionResponse.getCode(),regionResponse.getName());
        });
    }

    public Page<ChallengeResponse> getPageEs(SearchData searchData, Long userId) throws IOException {
        SearchRequest searchRequest = new SearchRequest(ESUtil.CHALLENGE_INDEX);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        if(!StringUtils.isEmpty(searchData.getKeyword())){
            boolQueryBuilder.should(QueryBuilders.multiMatchQuery(searchData.getKeyword(),"chaName"));
            boolQueryBuilder.should(QueryBuilders.multiMatchQuery(searchData.getKeyword(),"desc"));
            boolQueryBuilder.minimumShouldMatch(1);
        }

        if(null != searchData.getCategory()){
            boolQueryBuilder.must(QueryBuilders.nestedQuery("categories",QueryBuilders.matchQuery("categories.id",searchData.getCategory()),ScoreMode.Total));
        }

        if(!StringUtils.isEmpty(searchData.getRegion())){
            boolQueryBuilder.must(QueryBuilders.matchQuery("region",searchData.getRegion()));
        }

        searchSourceBuilder.query(boolQueryBuilder);
        // searchSourceBuilder.sort("userGrowth", SortOrder.DESC);
        String sortStr = "userGrowth";
        if(searchData.getSort() == 1){
            sortStr = "viewGrowth";
        }
        searchSourceBuilder.sort(sortStr, SortOrder.DESC);
        searchSourceBuilder.from((searchData.getPage()-1)*searchData.getPageSize());
        searchSourceBuilder.size(searchData.getPageSize());
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
        val value = searchResponse.getHits().getTotalHits().value;
        val hits = searchResponse.getHits().getHits();
        List<ChallengeResponse> challengeResponses = new ArrayList<>();

        ObjectMapper mapper = new ObjectMapper();
        for (SearchHit hit : hits) {

            ChallengeResponse challengeResponse = mapper.readValue(hit.getSourceAsString(),ChallengeResponse.class);
            // 图片替换
            if((challengeResponse.getCoverPhoto().indexOf("sign-sg") > 0 && challengeResponse.getCoverPhoto().indexOf("sign-sg") < 30) || (challengeResponse.getCoverPhoto().indexOf("sign-va") > 0 && challengeResponse.getCoverPhoto().indexOf("sign-va") < 30)){
                challengeResponse.setCoverPhoto(challengeResponse.getCoverPhoto().replace("sign-sg", "amd-va").replace("sign-va", "amd-va").replace("webp", "jpeg"));
            }

            if(null != userId){
                Optional<ChallengeCollection> challengeCollectionOptional = challengeCollectionRepository.findByCidAndUserId(challengeResponse.getCid(),userId);

                if(challengeCollectionOptional.isPresent()){
                    challengeResponse.setFavorite(true);
                    challengeResponse.setCollectId(challengeCollectionOptional.get().getId());
                }
            }

            challengeResponses.add(challengeResponse);
        }

        Page<ChallengeResponse> pageData = new Page<>(challengeResponses,searchData.getPage(),searchData.getPageSize(),(int)value/searchData.getPageSize(),(int)value);
        return pageData;
    }

    public ChallengeResponse getChallenge(long cid,Long userId) throws IOException {
        SearchRequest searchRequest = new SearchRequest(ESUtil.CHALLENGE_INDEX);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchQuery("cid",cid));
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = client.search(searchRequest,RequestOptions.DEFAULT);

        if(searchResponse.getHits().getHits().length == 0){
            return null;
        }
        ChallengeResponse challengeResponse = JsonUtil.jsonParse(ChallengeResponse.class,searchResponse.getHits().getHits()[0].getSourceAsString());

        if(null != userId){
            Optional<ChallengeCollection> challengeCollectionOptional = challengeCollectionRepository.findByCidAndUserId(challengeResponse.getCid(),userId);

            if(challengeCollectionOptional.isPresent()){
                challengeResponse.setFavorite(true);
                challengeResponse.setCollectId(challengeCollectionOptional.get().getId());
            }
        }
        return challengeResponse;
    }

    public PageResponse<BloggerAwemeResponse> getAwemePageByChallenge(long cid, int sortType,int page,int pageSize,Long userId) throws IOException {
        SearchRequest searchRequest = new SearchRequest(esUtil.getAwemeIndex());
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.should(QueryBuilders.nestedQuery("challenges",QueryBuilders.matchQuery("challenges.hid",cid), ScoreMode.Total));


        String sort = "playCount";

        switch (sortType){
            case 1:
                sort = "createTime";
                break;
            case 2:
                sort = "score";
                break;
            case 4:
                sort = "diggCount";
                break;
            case 5:
                sort = "commentCount";
                break;
            case 6:
                sort = "shareCount";
                break;
            default:
                break;
        }

        searchSourceBuilder.query(boolQueryBuilder);
         searchSourceBuilder.sort(sort, SortOrder.DESC);
        searchSourceBuilder.from((page-1) * pageSize);
        searchSourceBuilder.size(pageSize);
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        val value = searchResponse.getHits().getTotalHits().value;
        val hits = searchResponse.getHits().getHits();
        List<Map<String,Object>> data = new ArrayList<>();
        List<BloggerAwemeResponse> bloggerAwemeResponses = new ArrayList<>();

        for (SearchHit hit : hits) {
            val map = JsonUtil.jsonParse(Map.class, hit.getSourceAsString());
            // val blogger = bloggerRepository.findByOUid(Long.parseLong(map.get("uid").toString())).get();



            BloggerAwemeResponse bloggerAwemeResponse = new BloggerAwemeResponse();
            bloggerAwemeResponse.setId(Long.parseLong(map.get("id").toString()));
            bloggerAwemeResponse.setAwemeId(Long.parseLong(map.get("awemeId").toString()));
            bloggerAwemeResponse.setCover((String) map.get("cover"));
            bloggerAwemeResponse.setCreateTime(Long.parseLong( map.get("createTime").toString()));
            bloggerAwemeResponse.setDuration(map.get("duration").toString());
            bloggerAwemeResponse.setDynamicCover((String) map.get("dynamicCover"));
            bloggerAwemeResponse.setLikes(Long.parseLong(map.get("diggCount").toString()));
            bloggerAwemeResponse.setReviews(Long.parseLong( map.get("commentCount").toString()));
            bloggerAwemeResponse.setScore(Double.parseDouble(map.get("score").toString()));
            bloggerAwemeResponse.setShares(Long.parseLong( map.get("shareCount").toString()));
            bloggerAwemeResponse.setShareUrl((String) map.get("shareUrl"));
            bloggerAwemeResponse.setViews(Long.parseLong(map.get("playCount").toString()));
            bloggerAwemeResponse.setTitle((String) map.get("title"));

            if(null != userId){
                val awemeOptional = awemeCollectionRepository.findByAwemeIdAndUserId(Long.parseLong(map.get("awemeId").toString()), userId);

                if(awemeOptional.isPresent()){
                    bloggerAwemeResponse.setFavorite(true);
                    bloggerAwemeResponse.setCollectId(awemeOptional.get().getId());
                }else{
                    bloggerAwemeResponse.setFavorite(false);
                    bloggerAwemeResponse.setCollectId(null);
                }
            }

            bloggerAwemeResponses.add(bloggerAwemeResponse);
        }
        PageResponse<BloggerAwemeResponse> pageResponse = new PageResponse<BloggerAwemeResponse>(bloggerAwemeResponses,(int)value);
        return pageResponse;
    }

    public List<DistributionRecord> getRegionDistribution(long cid) throws IOException {
        String data = redisTemplate.opsForValue().get("dis_challenge_region_"+cid);
        List<DistributionRecord> distributionRecords = new ArrayList<>();

        if(StringUtils.isEmpty(data)){
            SearchRequest searchRequest = new SearchRequest(esUtil.getAwemeIndex());
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
            boolQueryBuilder.should(QueryBuilders.nestedQuery("challenges",QueryBuilders.matchQuery("challenges.hid",cid), ScoreMode.Total));
            searchSourceBuilder.query(boolQueryBuilder);
            TermsAggregationBuilder builder = AggregationBuilders.terms("regions").field("region").size(1000).executionHint("map");
            searchSourceBuilder.aggregation(builder);
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse =  client.search(searchRequest,RequestOptions.DEFAULT);

            if(null != searchResponse.getAggregations()){
                List<Map<String,Object>> mapList = new ArrayList<>();
                val hashtagName = (Terms) searchResponse.getAggregations().get("regions");
                val buckets = hashtagName.getBuckets();
                buckets.forEach( bucket -> {
                    Map<String,Object> map = new HashMap<>();
                    map.put("key",bucket.getKeyAsString());
                    map.put("value",bucket.getDocCount());
                    mapList.add(map);
                });
                distributionRecords =  getDistributionRecords(mapList);
                distributionRecords.removeIf( x -> StringUtils.isEmpty(x.getDescription()));
                redisTemplate.opsForValue().set("dis_challenge_region_"+cid,JsonUtil.parseJson(distributionRecords),7, TimeUnit.DAYS);
            }
        }else{

            ObjectMapper mapper = new ObjectMapper();
            distributionRecords = mapper.readValue(data, new TypeReference<List<DistributionRecord>>(){});
        }

        for (DistributionRecord distributionRecord : distributionRecords) {
            if(!"Others".equals(distributionRecord.getDescription())){
                distributionRecord.setDescription(regionMap.get(distributionRecord.getDescription()));
            }
        }


        return distributionRecords;
    }

    public List<DistributionRecord> getIndustryDistribution(long cid) throws IOException {
        String data = redisTemplate.opsForValue().get("dis_challenge_industry_"+cid);
        List<DistributionRecord> distributionRecords = new ArrayList<>();
        if(StringUtils.isEmpty(data)){
            SearchRequest searchRequest = new SearchRequest(esUtil.getAwemeIndex());
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
            boolQueryBuilder.should(QueryBuilders.nestedQuery("challenges",QueryBuilders.matchQuery("challenges.hid",cid), ScoreMode.Total));
            searchSourceBuilder.query(boolQueryBuilder);
            TermsAggregationBuilder builder = AggregationBuilders.terms("awemeTypes").field("awemeType").size(1000).executionHint("map");
            searchSourceBuilder.aggregation(builder);
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse =  client.search(searchRequest,RequestOptions.DEFAULT);

            if(null != searchResponse.getAggregations()){
                List<Map<String,Object>> mapList = new ArrayList<>();
                val hashtagName = (Terms) searchResponse.getAggregations().get("awemeTypes");
                val buckets = hashtagName.getBuckets();
                buckets.forEach( bucket -> {
                    Map<String,Object> map = new HashMap<>();
                    map.put("key",bucket.getKeyAsString());
                    map.put("value",bucket.getDocCount());
                    mapList.add(map);
                });
                distributionRecords =  getDistributionRecords(mapList);
                distributionRecords.removeIf( x -> StringUtils.isEmpty(x.getDescription()));
                redisTemplate.opsForValue().set("dis_challenge_industry_"+cid,JsonUtil.parseJson(distributionRecords),7, TimeUnit.DAYS);
            }
        }else {
            distributionRecords = JsonUtil.jsonParse(List.class,data);

        }
        return distributionRecords;
    }

    private List<DistributionRecord> getDistributionRecords(List<Map<String, Object>> mapList) {
        List<DistributionRecord> distributionRecords = new ArrayList<>();
        long sum = mapList.stream().mapToLong(m -> (long) m.get("value")).sum();
        int otherCount = 0;

        for (int i = 0; i < mapList.size(); i++) {
            if(i < 9){
                DistributionRecord distributionRecord = new DistributionRecord();
                distributionRecord.setDescription(mapList.get(i).get("key") == null ? "" : mapList.get(i).get("key").toString());

                BigDecimal bigDecimal = new BigDecimal((long)mapList.get(i).get("value"));
                double percent =bigDecimal.divide(new BigDecimal(sum),3, RoundingMode.FLOOR).multiply(new BigDecimal(100)).setScale(2).doubleValue();
                if(percent <= 0){
                    continue;
                }
                distributionRecord.setPercent(percent);
                distributionRecords.add(distributionRecord);
            }else{
                otherCount += (long)mapList.get(i).get("value");
            }
        }

        if (otherCount > 0) {
            DistributionRecord distributionRecord = new DistributionRecord();
            distributionRecord.setDescription("Others");
            BigDecimal bigDecimal = new BigDecimal(otherCount);
            double percent =bigDecimal.divide(new BigDecimal(sum),3, RoundingMode.FLOOR).multiply(new BigDecimal(100)).setScale(2).doubleValue();
            distributionRecord.setPercent(percent);
            distributionRecords.add(distributionRecord);
        }

        distributionRecords.sort((o1,o2) ->{
            if(o1.getPercent() > o2.getPercent()){
                return -1;
            }else if(o1.getPercent() < o2.getPercent()){
                return 1;
            }
            return 0;
        });
        return distributionRecords;
    }
}
